#include "theaterconst.h"

/* Customer */
void main() {
	int i, j;
	int customerNumber;
	int sum = 0;
	int groupNumber;
	int popcorn, soda;
	int bathroom;
	int bathroomTime;
	
	/* initialize global variables */
	init();
	
	/* retrieve next Customer ID */
	Acquire(idMutex);
	customerNumber = ReadMonitor(customerID, 0);
	SetMonitor(customerID, 0, customerNumber + 1);
	
	for(i = 0;; i++) {
		sum += CUSTOMERS_PER_GROUP - 1;
		if (sum > customerNumber) break;
	}
	groupNumber = i;
	customerNumber += groupNumber+1;
	Release(idMutex);
	
	/* the customer arrives */
	/* Acquire(outputLock); */
	prints("Customer "); print(customerNumber); 
	prints(" in Group "); print(groupNumber); 
	prints(" has entered the movie theater.\n");
	/* Release(outputLock); */
	Acquire(groupLock[groupNumber]);

	/* then wait for the head customer to buy the ticket */
	/* Acquire(outputLock); */
	prints("Customer "); print(customerNumber); 
	prints(" of Group "); print(groupNumber); 
	prints(" is waiting for the HeadCustomer.\n");
	/* Release(outputLock); */
	
	SetMonitor(groupMV, groupNumber, ReadMonitor(groupMV, groupNumber) + 1);
	prints("Customer "); print(customerNumber); prints(" in Group "); print(groupNumber); prints(" sees that "); print(ReadMonitor(groupMV, groupNumber)); prints(" Customers are here...\n");
	if(ReadMonitor(groupMV, groupNumber) == CUSTOMERS_PER_GROUP - 1) {
		prints("Customer "); print(customerNumber); prints(" in Group "); print(groupNumber); prints(" sees that all Customers are here, signalling HeadCustomer...\n");
		Signal(hcCV[groupNumber], groupLock[groupNumber]);
	}
	Wait(groupCV[groupNumber], groupLock[groupNumber]);
	
	/* Acquire(outputLock); */
	prints("Customer "); print(customerNumber); 
	prints(" of Group "); print(groupNumber); 
	prints(" has been told by the HeadCustomer to proceed.\n");
	/* Release(outputLock); */

	popcorn = Rand(4);
	soda = Rand(4);
  	/* tell the head if the customer wants any food */
	if (popcorn != 0) {
		SetMonitor(customerPopcorn, customerNumber, 1);
	}
	if (soda != 0) {
		SetMonitor(customerSoda, customerNumber, 1);
	}
	
	/* Acquire(outputLock); */
	prints("Customer "); print(customerNumber);
	prints(" in Group "); print(groupNumber);
	prints(" wants "); print(ReadMonitor(customerPopcorn, customerNumber));
	prints(" popcorn and "); print(ReadMonitor(customerSoda, customerNumber));
	prints(" soda.\n");
	/* Release(outputLock); */
	
	SetMonitor(groupMV, groupNumber, ReadMonitor(groupMV, groupNumber) + 1);
	if(ReadMonitor(groupMV, groupNumber) == CUSTOMERS_PER_GROUP - 1)
		Signal(hcCV[groupNumber], groupLock[groupNumber]);
	
	/* wait the head to buy food */
	/* Acquire(outputLock); */
	prints("Customer "); print(customerNumber); 
	prints(" of Group "); print(groupNumber); 
	prints(" is waiting for the HeadCustomer.\n");
	/* Release(outputLock); */
	
	Wait(groupCV[groupNumber], groupLock[groupNumber]);

	/* Acquire(outputLock); */
	prints("Customer "); print(customerNumber); 
	prints(" of Group "); print(groupNumber); 
	prints(" has been told by the HeadCustomer to proceed.\n");
	/* Release(outputLock); */

	while (ReadMonitor(customerSeat, customerNumber) == 0) {
		/* there's no seat, go to lobby */
		/* Acquire(outputLock); */
		prints("Customer "); print(customerNumber);
		prints(" in Group "); print(groupNumber);
		prints(" is in the lobby.\n") ;
		/* Release(outputLock); */
		
		SetMonitor(groupMV, groupNumber, ReadMonitor(groupMV, groupNumber) + 1);
		if (ReadMonitor(groupMV, groupNumber) == CUSTOMERS_PER_GROUP - 1)
			Signal(hcCV[groupNumber], groupLock[groupNumber]);
		
		/* wait the head to buy food */
		/* Acquire(outputLock); */
		prints("Customer "); print(customerNumber); 
		prints(" of Group "); print(groupNumber); 
		prints(" is waiting for the HeadCustomer.\n");
		/* Release(outputLock); */
	
		Wait(groupCV[groupNumber], groupLock[groupNumber]);

		/* Acquire(outputLock); */
		prints("Customer "); print(customerNumber); 
		prints(" of Group "); print(groupNumber); 
		prints(" has been told by the HeadCustomer to proceed.\n");
		/* Release(outputLock); */
		
		/* Acquire(outputLock); */
		prints("Customer "); print(customerNumber);
		prints(" in Group "); print(groupNumber);
		prints(" is leaving the lobby.\n");
		/* Release(outputLock); */
				
		/* wait the head to buy food */
		/* Acquire(outputLock); */
		prints("Customer "); print(customerNumber); 
		prints(" of Group "); print(groupNumber); 
		prints(" is waiting for the HeadCustomer.\n");
		/* Release(outputLock); */
		
		SetMonitor(groupMV, groupNumber, ReadMonitor(groupMV, groupNumber) + 1);
		if (ReadMonitor(groupMV, groupNumber) == CUSTOMERS_PER_GROUP - 1) {
			Signal(hcCV[groupNumber], groupLock[groupNumber]);			
		}
		
	
		Wait(groupCV[groupNumber], groupLock[groupNumber]);

		/* Acquire(outputLock); */
		prints("Customer "); print(customerNumber); 
		prints(" of Group "); print(groupNumber); 
		prints(" has been told by the HeadCustomer to proceed.\n");
		/* Release(outputLock); */
	}
	
	/* Acquire(outputLock); */
	prints("Customer "); print(customerNumber);
	prints(" in Group "); print(groupNumber);
	prints(" has been seated\n ");
	Release(groupLock[groupNumber]);

	/* see the movie */
	Acquire(movieLock);
	/* Acquire(outputLock); */
	prints("Customer "); print(customerNumber);
	prints(" in Group "); print(groupNumber);
	prints(" is sitting in a theater room seat.\n");
	/* Release(outputLock); */

	SetMonitor(customerReadyWatchingMovie, 0, ReadMonitor(customerReadyWatchingMovie, 0) + 1);
	
	prints("Customer "); print(customerNumber);
	prints(" in Group "); print(groupNumber);
	prints(" sees "); print(ReadMonitor(customerReadyWatchingMovie, 0));
	prints(" Customers sitting in the screening room\n");
		
	/* Watch the movie */
	Wait(movieCV, movieLock);
	SetMonitor(customerReadyWatchingMovie, 0, ReadMonitor(customerReadyWatchingMovie, 0) - 1);
	
	prints("Customer "); print(customerNumber);
	prints(" in Group "); print(groupNumber);
	prints(" sees "); print(ReadMonitor(customerReadyWatchingMovie, 0));
	prints(" Customers remaining in the screening room\n");
	
	/* Acquire(outputLock); */
	prints("Customer "); print(customerNumber);
	prints(" in Group "); print(groupNumber);
	prints(" is getting out of a theater room seat.\n");
	/* Release(outputLock); */
	Release(movieLock);

	/* tell the head if the customer wants to go to the bathroom */
	Acquire(groupLock[groupNumber]);
	bathroom = Rand(4);
	if (bathroom == 0) {
		SetMonitor(customerBathroomNumber, groupNumber, ReadMonitor(customerBathroomNumber, groupNumber) + 1);
	}
	Release(groupLock[groupNumber]);
	if (bathroom == 0) {
		/* go to the bathroom */
		Acquire(groupLock[groupNumber]);
		SetMonitor(groupMV, groupNumber, ReadMonitor(groupMV, groupNumber) + 1);
		if (ReadMonitor(groupMV, groupNumber) == CUSTOMERS_PER_GROUP - 1)
			Signal(hcCV[groupNumber], groupLock[groupNumber]);
		Release(groupLock[groupNumber]);

		Acquire(bathroomLock);
		/* Acquire(outputLock); */
		prints("Customer "); print(customerNumber);
		prints(" in Group "); print(groupNumber);
		prints(" is going to the bathroom.\n");
		/* Release(outputLock); */
		bathroomTime = Rand(5);
		for(i = 0; i < bathroomTime; i++) {
			Yield();
		}
		/* Acquire(outputLock); */
		prints("Customer "); print(customerNumber);
		prints(" in Group "); print(groupNumber);
		prints(" is leaving to the bathroom.\n");
		/* Release(outputLock); */
		Release(bathroomLock);
		
		Acquire(groupLock[groupNumber]);
		SetMonitor(groupBathroomMV, groupNumber, ReadMonitor(groupBathroomMV, groupNumber) + 1);
		if (ReadMonitor(groupBathroomMV, groupNumber) == ReadMonitor(customerBathroomNumber, groupNumber))
		{
			Signal(groupBathroomCV[groupNumber], groupLock[groupNumber]);
		}
	} else {
		Acquire(groupLock[groupNumber]);
		SetMonitor(groupMV, groupNumber, ReadMonitor(groupMV, groupNumber) + 1);
		if (ReadMonitor(groupMV, groupNumber) == CUSTOMERS_PER_GROUP - 1)
			Signal(hcCV[groupNumber], groupLock[groupNumber]);
	}

 	/* wait for every one comes out of the bathroom */
	/* Acquire(outputLock); */
	prints("Customer "); print(customerNumber);
	prints(" in Group "); print(groupNumber);
	prints(" is in the lobby.\n");
	/* Release(outputLock); */
	
	/* Acquire(outputLock); */
	prints("Customer "); print(customerNumber); 
	prints(" of Group "); print(groupNumber); 
	prints(" is waiting for the HeadCustomer.\n");
	/* Release(outputLock); */
	
	Wait(groupCV[groupNumber], groupLock[groupNumber]);
	
	/* Acquire(outputLock); */
	prints("Customer "); print(customerNumber); 
	prints(" of Group "); print(groupNumber); 
	prints(" has been told by the HeadCustomer to proceed.\n");
	/* Release(outputLock); */
	
	/* Acquire(outputLock); */
	prints("Customer "); print(customerNumber);
	prints(" in Group "); print(groupNumber);
	prints(" is leaving the lobby.\n");
	/* Release(outputLock); */	
	
	Release(groupLock[groupNumber]);
	
	/* all set, ready to leave. the customer leaves */
	Acquire(leaveMutex);
	SetMonitor(leaveCount, 0, ReadMonitor(leaveCount, 0) + 1);
	/* Acquire(outputLock); */
	prints("Customer "); print(customerNumber);
	prints(" in Group "); print(groupNumber);
	prints(" has left the movie theater.\n");
	/* Release(outputLock); */
	Release(leaveMutex);
	Exit(0);
}